/*
 * BlackLightning.PluginLib.LoginClients.AutoLogin
 * Andy Tidball
 * 
 * Project: Black Lightning Plugin Library
 * Copyright: GNU General Public License
 */

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using BlackLightning.PluginLib;
using BlackLightning.PluginLib.LoginClients;

[assembly: BLLoginClient(typeof(AutoLogin))]

namespace BlackLightning.PluginLib.LoginClients {
	/// <summary>
	/// A login client that takes all the necessary information up front and walks through the entire login process automatically.
	/// </summary>
	public class AutoLogin : LoginClient {
		// constants
		private const string HelpText = "A login client that automatically logs in using the information provided up front.";

		// member data
		private string  _Username = null; // the username to login with
		private string  _Password = null; // the password to login with
		private string  _GameCode = null; // the code of the game to login to
		private string  _CharCode = null; // the code of the character to login with
		private string  _FECode   = null; // the code of the front end to use
		private SalFile _SalFile  = null; // the SAL file retrieved from the login server

		// member data - controls
		private Label     lblTitle;
		private StatusBar stbStatus;

		/// <summary>
		/// Creates a new AutoLogin.
		/// </summary>
		public AutoLogin() {
			// initialize our controls
			InitializeControls();
		}

		/// <summary>
		/// Gets the value of a property of the login client.
		/// </summary>
		/// <param name="Property">The property to retrieve the value of.</param>
		/// <returns>The value of the given property, or null if the login client doesn't use that property.</returns>
		public override object GetProperty(LoginClientProperty Property) {
			switch (Property) {
				case LoginClientProperty.HelpText: return HelpText;
				case LoginClientProperty.Username: return _Username;
				case LoginClientProperty.Password: return _Password;
				case LoginClientProperty.GameCode: return _GameCode;
				case LoginClientProperty.CharCode: return _CharCode;
				case LoginClientProperty.FECode:   return _FECode;
				default:                           return null;
			}
		}

		/// <summary>
		/// Called by Black Lightning whenever it has a private message for us.
		/// </summary>
		/// <param name="Message">The message being sent by Black Lightning.</param>
		public override void HandleMessage(Message Message) {
			// we don't care about messages
		}

		/// <summary>
		/// Provides the login logic for any specific implementation of LoginClient.
		/// </summary>
		/// <param name="Args">The command-line arguments passed to Black Lightning for use logging in.</param>
		/// <returns>An SalFile that can be used to login to a game and play.</returns>
		/// <remarks>
		/// <para>The following four arguments must be provided:</para>
		/// <list type="bullet">
		///		<item><description>username=(username): The username to login with.</description></item>
		///		<item><description>password=(password): The password to login with.</description></item>
		///		<item><description>gamecode=(gamecode): The code of the game to login to.</description></item>
		///		<item><description>charcode=(charcode): The code of the character to login with.</description></item>
		///		<item><description>fecode=(fecode): The code of the front end (STORM or WIZ) to play with.</description></item>
		/// </list>
		/// </remarks>
		public override SalFile Login(string[] Args) {
			// parse the arguments
			foreach (string Arg in Args) {
				string[] Split = Arg.Split('=');
				if (Split.Length != 2) {
					continue;
				}
				switch (Split[0].ToLower()) {
					case "username": _Username = Split[1]; break;
					case "password": _Password = Split[1]; break;
					case "gamecode": _GameCode = Split[1]; break;
					case "charcode": _CharCode = Split[1]; break;
					case "fecode":   _FECode   = Split[1]; break;
				}
			}

			// show the dialog
			if (ShowDialog() != DialogResult.OK) {
				return null;
			}

			// return the SAL file that we grabbed
			return _SalFile;
		}

		/// <summary>
		/// Creates and initializes all of our child controls.
		/// </summary>
		public void InitializeControls() {
			// create our controls
			stbStatus = new StatusBar();
			lblTitle  = new Label();

			// stbStatus
			stbStatus.Location = new Point(0, 56);
			stbStatus.Name = "stbStatus";
			stbStatus.Size = new Size(292, 22);
			stbStatus.SizingGrip = false;
			stbStatus.Text = "Welcome to Black Lightning";

			// lblTitle
			lblTitle.Font = new Font("Microsoft Sans Serif", 24F, FontStyle.Regular, GraphicsUnit.Point, ((System.Byte)(0)));
			lblTitle.Location = new Point(8, 8);
			lblTitle.Name = "lblTitle";
			lblTitle.Size = new Size(276, 40);
			lblTitle.Text = "Automatic Login";
			lblTitle.TextAlign = ContentAlignment.MiddleCenter;

			// this
			AutoScaleDimensions = new Size(5, 13);
			BackColor = SystemColors.Control;
			ClientSize = new Size(292, 78);
			DialogResult = DialogResult.None;
			FormBorderStyle = FormBorderStyle.FixedSingle;
			MaximizeBox = false;
			Name = "AutoLogin";
			Text = "Black Lightning";
			Controls.Add(stbStatus);
			Controls.Add(lblTitle);
			Shown  += this_Shown;
		}

		/// <summary>
		/// Sets the text to display in the status bar.
		/// </summary>
		/// <param name="Status">The text to display in the status bar.</param>
		private void SetStatus(string Status) {
			stbStatus.Text = Status;
		}

		/// <summary>
		/// Called when the form is first displayed.
		/// </summary>
		/// <param name="Sender">The form being displayed.</param>
		/// <param name="e">No arguments.</param>
		public void this_Shown(object Sender, EventArgs e) {
			// make sure we got everything we needed
			if (_Username == null) {
				SetStatus("No username specified.");
				return;
			}
			if (_Password == null) {
				SetStatus("No password specified.");
				return;
			}
			if (_GameCode == null) {
				SetStatus("No game code specified.");
				return;
			}
			if (_CharCode == null) {
				SetStatus("No character code specified.");
				return;
			}
			if (_FECode == null) {
				SetStatus("No front end specified.");
				return;
			}
			if (_FECode == "StormFront") {
				_FECode = "STORM";
			} else if (_FECode == "Wizard") {
				_FECode = "WIZ";
			}
			if (_FECode != "STORM" && _FECode != "WIZ") {
				SetStatus("Unrecognized front end code: " + _FECode);
				return;
			}

			// refresh to make sure all of our controls get drawn before we start any blocking networking calls
			Refresh();

			// connect to the login server
			SetStatus("Connecting to " + LoginClient.LoginServerHost + ":" + LoginClient.LoginServerPort + "...");
			Connect();

			// grab an encryption key for the password
			SetStatus("Requesting encryption key...");
			string EncryptionKey = GetEncryptionKey();

			// encrypt the entered password
			SetStatus("Encrypting password...");
			string EncryptedPassword = EncryptPassword(_Password, EncryptionKey);

			// authenticate the username and password
			SetStatus("Authenticating username: " + _Username + "...");
			try {
				Authenticate(_Username, EncryptedPassword);
			} catch (System.Security.SecurityException x) {
				SetStatus("Error: " + x.Message);
				return;
			}

			// grab a list of games
			SetStatus("Retrieving game list...");
			List<GameInfo> Games = GetGameList();
			
			// find the game we have a code for
			string GameName = null;
			string GameCode = null;
			foreach (GameInfo Game in Games) {
				if (Game.Code == _GameCode) {
					GameName = Game.Name;
					GameCode = Game.Code;
					break;
				}
			}
			if (GameName == null) {
				// check if we have a game name instead
				foreach (GameInfo Game in Games) {
					if (Game.Name == _GameCode) {
						GameName = Game.Name;
						GameCode = Game.Code;
						break;
					}
				}
			}
			if (GameName == null) {
				SetStatus("Unrecognized game code: " + _GameCode);
				return;
			}

			// select the game on the server
			SetStatus("Logging into game: " + GameName + "...");
			SelectGame(GameCode);

			// grab a character list for the selected game
			SetStatus("Retrieving character list...");
			CharacterList Chars = GetCharacterList();

			// find the character we have a code for
			string CharName = null;
			string CharCode = null;
			foreach (KeyValuePair<string, string> Char in Chars.Characters) {
				if (Char.Key == _CharCode) {
					CharName = Char.Value;
					CharCode = Char.Key;
					break;
				}
			}
			if (CharName == null) {
				// check if we have a character name instead
				foreach (KeyValuePair<string, string> Char in Chars.Characters) {
					if (Char.Value == _CharCode) {
						CharName = Char.Value;
						CharCode = Char.Key;
						break;
					}
				}
			}
			if (CharName == null) {
				SetStatus("Unrecognized character code: " + _CharCode);
				return;
			}

			// get an SAL file
			SetStatus("Logging in with character: " + CharName + "...");
			_SalFile = GetSalFile(CharCode, CharName, (_FECode == "STORM" ? FrontEnd.StormFront : FrontEnd.Wizard));

			// disconnect from the server
			SetStatus("Disconnecting...");
			Disconnect();

			// close the window with an OK result
			SetStatus("Launching " + (_FECode == "STORM" ? "StormFront" : "Wizard") + "...");
			DialogResult = DialogResult.OK;
			Close();
		}
	}
}
